use super::ast::Node;
use super::error::{CalcError, CalcResult};
use super::lexer::Lexer;
use super::token::{OperatorPrecedence, Token};

pub struct Parser<'a> {
   lexer: Lexer<'a>,
   current_token: Token,
}

impl<'a> Parser<'a> {
   pub fn new(input: &'a str) -> CalcResult<Self> {
      let mut lexer = Lexer::new(input);
      let current_token = lexer.next().ok_or_else(|| {
         CalcError::UnexpectedChar(lexer.get_unexpected_char().unwrap())
      })?;

      Ok(Parser {
         lexer,
         current_token,
      })
   }

   pub fn parse(&mut self) -> CalcResult<Node> {
      self.parse_expression(OperatorPrecedence::Default)
   }

   fn next_token(&mut self) -> CalcResult<()> {
      self.current_token = self.lexer.next().ok_or_else(|| {
         CalcError::UnexpectedChar(self.lexer.get_unexpected_char().unwrap())
      })?;

      Ok(())
   }

   // 第一个字符可能是数字,负号"-",括号"("
   fn parse_expression(
      &mut self,
      precedence: OperatorPrecedence,
   ) -> CalcResult<Node> {
      let mut expr = self.parse_number_or_expression()?;

      while precedence < self.current_token.get_precedence() {
         // 如果后面运算符的优先级比前面的大,就继续往后解析
         expr = self.parse_binary_expression(expr)?;
      }

      Ok(expr)
   }

   fn parse_binary_expression(&mut self, left_expr: Node) -> CalcResult<Node> {
      match self.current_token {
         Token::Add => {
            self.next_token()?;
            // 解析运算符后的表达是或者数字
            let right_expr =
               self.parse_expression(OperatorPrecedence::AddOrSubtract)?;
            Ok(Node::Add(Box::new(left_expr), Box::new(right_expr)))
         }
         Token::Subtract => {
            self.next_token()?;
            // 解析运算符后的表达是或者数字
            let right_expr =
               self.parse_expression(OperatorPrecedence::AddOrSubtract)?;
            Ok(Node::Subtract(Box::new(left_expr), Box::new(right_expr)))
         }
         Token::Multiply => {
            self.next_token()?;
            // 解析运算符后的表达是或者数字
            let right_expr =
               self.parse_expression(OperatorPrecedence::MultiplyOrDivide)?;
            Ok(Node::Multiply(Box::new(left_expr), Box::new(right_expr)))
         }
         Token::Divide => {
            self.next_token()?;
            // 解析运算符后的表达是或者数字
            let right_expr =
               self.parse_expression(OperatorPrecedence::MultiplyOrDivide)?;
            Ok(Node::Divide(Box::new(left_expr), Box::new(right_expr)))
         }
         Token::Caret => {
            //  幂运算
            self.next_token()?;
            // 解析运算符后的表达是或者数字
            let right_expr = self.parse_expression(OperatorPrecedence::Power)?;
            Ok(Node::Power(Box::new(left_expr), Box::new(right_expr)))
         }
         // _ => Ok(left_expr),
         _ => unreachable!(),
      }
   }

   fn parse_number_or_expression(&mut self) -> CalcResult<Node> {
      match self.current_token {
         Token::Number(num) => {
            self.next_token()?;
            Ok(Node::Number(num))
         }
         Token::Subtract => {
            self.next_token()?;
            let expr = self.parse_expression(OperatorPrecedence::Negative)?;
            Ok(Node::Negative(Box::new(expr)))
         }
         Token::LeftParen => {
            self.next_token()?;
            // 解析括号中的表达式
            let expr = self.parse_expression(OperatorPrecedence::Default)?;
            if self.current_token != Token::RightParen {
               if self.current_token == Token::Eof {
                  return Err(CalcError::InvalidOperator(
                     "不完整的表达式".to_string(),
                  ));
               }

               return Err(CalcError::InvalidOperator(format!(
                  "期望 ')', 但是得到 '{}'",
                  self.current_token
               )));
            }
            self.next_token()?;
            Ok(expr)
         }
         _ => {
            if self.current_token == Token::Eof {
               return Err(CalcError::InvalidOperator(
                  "不完整的表达式".to_string(),
               ));
            }
            Err(CalcError::InvalidOperator(format!(
               "期望是数字或者表达式, 但是得到 '{}'",
               self.current_token
            )))
         }
      }
   }
}

#[cfg(test)]
mod tests {
   use rust_decimal::dec;

   use super::*;

   #[test]
   fn test_parser() {
      let input = "1 + 2 * 3";
      let mut parser = Parser::new(input).unwrap();
      assert_eq!(
         parser.parse(),
         Ok(Node::Add(
            Box::new(Node::Number(dec!(1))),
            Box::new(Node::Multiply(
               Box::new(Node::Number(dec!(2))),
               Box::new(Node::Number(dec!(3)))
            ))
         ))
      );
   }
}
